//
//  TMWikipediaDefinerMacro.m
//  TMWikipediaDefiner
//
//  Created by Anshu Chimala on 1/10/10.
//  Copyright 2010 Home. All rights reserved.
//

#import "TMWikipediaDefinerMacro.h"
#import "JSON.h"
#import "HTTPOperation.h"

// Eheheh
@interface NSString (Awful)
+ (NSString *)flattenHTML:(NSString *)html;
+ (NSString *)decodeHTMLEntities:(NSString *)source;
- (NSString *)stringByCompressingWhitespaceTo:(NSString *)seperator;
@end
@implementation NSString (Awful)
- (NSString *)stringByCompressingWhitespaceTo:(NSString *)seperator
{
	NSArray *comps = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
	NSMutableArray *nonemptyComps = [[[NSMutableArray alloc] init] autorelease];
	
	// only copy non-empty entries
	for (NSString *oneComp in comps)
	{
		if (![oneComp isEqualToString:@""])
		{
			[nonemptyComps addObject:oneComp];
		}
		
	}
	
	return [nonemptyComps componentsJoinedByString:seperator];  // already marked as autoreleased
}

+ (NSString *)flattenHTML:(NSString *)html {
    NSScanner *theScanner;
    NSString *text = nil;
	
    theScanner = [[NSScanner alloc] initWithString:html];
    while (!theScanner.isAtEnd) {
        [theScanner scanUpToString:@"<" intoString:NULL]; 
        [theScanner scanUpToString:@">" intoString:&text];
        html = [html stringByReplacingOccurrencesOfString:
				[NSString stringWithFormat:@"%@>", text]
											   withString:@""];
    }
	
	[theScanner release];
	theScanner = [[NSScanner alloc] initWithString:html];
    while (!theScanner.isAtEnd) {
        [theScanner scanUpToString:@"(" intoString:NULL]; 
        [theScanner scanUpToString:@")" intoString:&text];
        html = [html stringByReplacingOccurrencesOfString:
				[ NSString stringWithFormat:@"%@)", text]
											   withString:@""];
    }
    
    return [html stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

+ (NSString *)decodeHTMLEntities:(NSString *)source
{ 
	if(!source) return nil;
	else if([source rangeOfString: @"&"].location == NSNotFound) return source;
	else
	{
		
		NSMutableString *escaped = [NSMutableString stringWithString: source];
		
		
		NSArray *entities = [NSArray arrayWithObjects: 
							 @"&amp;", @"&lt;", @"&gt;", @"&quot;",
							 /* 160 = nbsp */
							 @" ", @"&iexcl;", @"&cent;", @"&pound;", @"&curren;", @"&yen;", @"&brvbar;",
							 @"&sect;", @"&uml;", @"&copy;", @"&ordf;", @"&laquo;", @"&not;", @"&shy;", @"&reg;",
							 @"&macr;", @"&deg;", @"&plusmn;", @"&sup2;", @"&sup3;", @"&acute;", @"&micro;",
							 @"&para;", @"&middot;", @"&cedil;", @"&sup1;", @"&ordm;", @"&raquo;", @"&frac14;",
							 @"&frac12;", @"&frac34;", @"&iquest;", @"&Agrave;", @"&Aacute;", @"&Acirc;",
							 @"&Atilde;", @"&Auml;", @"&Aring;", @"&AElig;", @"&Ccedil;", @"&Egrave;",
							 @"&Eacute;", @"&Ecirc;", @"&Euml;", @"&Igrave;", @"&Iacute;", @"&Icirc;", @"&Iuml;",
							 @"&ETH;", @"&Ntilde;", @"&Ograve;", @"&Oacute;", @"&Ocirc;", @"&Otilde;", @"&Ouml;",
							 @"&times;", @"&Oslash;", @"&Ugrave;", @"&Uacute;", @"&Ucirc;", @"&Uuml;", @"&Yacute;",
							 @"&THORN;", @"&szlig;", @"&agrave;", @"&aacute;", @"&acirc;", @"&atilde;", @"&auml;",
							 @"&aring;", @"&aelig;", @"&ccedil;", @"&egrave;", @"&eacute;", @"&ecirc;", @"&euml;",
							 @"&igrave;", @"&iacute;", @"&icirc;", @"&iuml;", @"&eth;", @"&ntilde;", @"&ograve;",
							 @"&oacute;", @"&ocirc;", @"&otilde;", @"&ouml;", @"&divide;", @"&oslash;", @"&ugrave;",
							 @"&uacute;", @"&ucirc;", @"&uuml;", @"&yacute;", @"&thorn;", @"&yuml;", nil];
		
		NSArray *characters = [NSArray arrayWithObjects:@"&", @"<", @">", @"\"", nil];
		
		int i, count = [entities count], characterCount = [characters count];
		
		// Html
		for(i = 0; i < count; i++)
		{
			NSRange range = [source rangeOfString: [entities objectAtIndex:i]];
			if(range.location != NSNotFound)
			{
				if (i < characterCount)
				{
					[escaped replaceOccurrencesOfString:[entities objectAtIndex: i] 
											 withString:[characters objectAtIndex:i] 
												options:NSLiteralSearch 
												  range:NSMakeRange(0, [escaped length])];
				}
				else
				{
					[escaped replaceOccurrencesOfString:[entities objectAtIndex: i] 
											 withString:[NSString stringWithFormat: @"%C", (160-characterCount) + i] 
												options:NSLiteralSearch 
												  range:NSMakeRange(0, [escaped length])];
				}
			}
		}
		
		// Decimal & Hex
		NSRange start, finish, searchRange = NSMakeRange(0, [escaped length]);
		i = 0;
		
		while(i < [escaped length])
		{
			start = [escaped rangeOfString: @"&#" 
								   options: NSCaseInsensitiveSearch 
									 range: searchRange];
			
			finish = [escaped rangeOfString: @";" 
									options: NSCaseInsensitiveSearch 
									  range: searchRange];
			
			if(start.location != NSNotFound && finish.location != NSNotFound &&
			   finish.location > start.location)
			{
				NSRange entityRange = NSMakeRange(start.location, (finish.location - start.location) + 1);
				NSString *entity = [escaped substringWithRange: entityRange];     
				NSString *value = [entity substringWithRange: NSMakeRange(2, [entity length] - 2)];
				
				[escaped deleteCharactersInRange: entityRange];
				
				if([value hasPrefix: @"x"])
				{
					unsigned tempInt = 0;
					NSScanner *scanner = [NSScanner scannerWithString: [value substringFromIndex: 1]];
					[scanner scanHexInt: &tempInt];
					[escaped insertString: [NSString stringWithFormat: @"%C", tempInt] atIndex: entityRange.location];
				}
				else
				{
					[escaped insertString: [NSString stringWithFormat: @"%C", [value intValue]] atIndex: entityRange.location];
				}
				i = start.location;
			}
			else i++;
			searchRange = NSMakeRange(i, [escaped length] - i);
		}
		
		return escaped;    // Note this is autoreleased
	}
}
@end



@implementation TMWikipediaDefinerMacro
@synthesize delegate = _delegate;

- (NSString *)title {
	return @"Define with Wikipedia";
}

- (NSString *)author {
	return @"Anshu Chimala";
}

- (NSString *)description {
	return @"Grab the definition of something through its Wikipedia article. Very useful if your history teacher decides to assign a bunch of random vocabulary.";
}

- (NSString *)beforeText {
	return @":( WHAT IS SOCIAL LEARNING THEORY OMG THIS ASSIGNMENT IS DUE TOMORROW";
}

- (NSString *)afterText {
	return @"lol I pasted it from Wikipedia and got an A+";
}

- (NSString *)progressMessage {
	NSString *messages[] = {
		@"Searching...",
		@"Querying Wikipedia...",
		@"Parsing..."
	};
	return messages[_stage%3];
}

- (void)processText:(NSString *)text {
	// First, we perform a Google search with the given text to find the
	// Wikipedia article link, because Wikipedia's search engine is teh suck.
	
	_stage = 0;
	_delegate.shouldPasteAfterwards = YES;
	_delegate.showActivityIndicator = YES;
	
	HTTPOperation *hop = [[HTTPOperation alloc] init];
	hop.delegate = self;
	hop.successSelector = @selector(gotGoogleResults:);
	hop.failureSelector = @selector(httpFailure:);
	hop.targetURL = [NSURL URLWithString:[@"http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=site:wikipedia.org%20" stringByAppendingString:[text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
	[hop start];
	[hop release];
}

- (void)gotGoogleResults:(NSData *)data {
	_stage = 1;
	_delegate.showActivityIndicator = YES; // Force it to update the message.

	@try {
		NSString *str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
		
		NSDictionary *dict = [str JSONValue];
		NSArray *results = [[dict objectForKey:@"responseData"] objectForKey:@"results"];
		if([results count] <= 0) {
			_delegate.showActivityIndicator = NO;
			[_delegate macro:self didFailWithErrorMessage:@"No results found."];
			return;
		}
		NSDictionary *result = [results objectAtIndex:0];
		NSString *wikiURL = [[result objectForKey:@"unescapedUrl"] lastPathComponent];
		
		HTTPOperation *hop = [[HTTPOperation alloc] init];
		hop.delegate = self;
		hop.successSelector = @selector(gotWikiData:);
		hop.failureSelector = @selector(httpFailure:);
		hop.targetURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://en.wikipedia.org/w/api.php?action=query&prop=revisions&titles=%@&rvprop=content&redirects=1&format=json", wikiURL]];
		[hop start];
		[hop release];
	}
	@catch (NSException *ex) {
		[_delegate macro:self didFailWithErrorMessage:[ex reason]];
	}
}

- (void)gotWikiData:(NSData *)data {
	//query/pages/<first thing>/revisions/0/*/
	
	_stage = 2;
	_delegate.showActivityIndicator = YES;
	
	@try {
		NSString *str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
		NSDictionary *dict = [str JSONValue];
		dict = [[dict objectForKey:@"query"] objectForKey:@"pages"];
		NSString *pageid = [[dict allKeys] objectAtIndex:0];
		if([pageid intValue]<0) {
			[_delegate macro:self didFailWithErrorMessage:@"Wikipedia page not found."];
			return;
		}
		NSString *content = [[[[dict objectForKey:pageid] objectForKey:@"revisions"] objectAtIndex:0] objectForKey:@"*"];
		content = [[content componentsSeparatedByString:@"\n"] objectAtIndex:0];
		
		HTTPOperation *hop = [[HTTPOperation alloc] init];
		hop.delegate = self;
		hop.successSelector = @selector(gotParseData:);
		hop.failureSelector = @selector(httpFailure:);
		hop.targetURL = [NSURL URLWithString:[@"http://en.wikipedia.org/w/api.php?action=parse&prop=text&format=json&text=" stringByAppendingString:[content stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
		[hop start];
		[hop release];
	}
	@catch (NSException *ex) {
		[_delegate macro:self didFailWithErrorMessage:[ex reason]];
	}
}

- (void)gotParseData:(NSData *)data {
	//parse/text/*
	
	_delegate.showActivityIndicator = NO;
	
	@try {
		NSString *str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
		NSDictionary *dict = [str JSONValue];
		NSString *result = [[[dict objectForKey:@"parse"] objectForKey:@"text"] objectForKey:@"*"];
		result = [NSString decodeHTMLEntities:result];
		[_delegate macro:self didProcessTextWithResult:[[NSString flattenHTML:result] stringByCompressingWhitespaceTo:@" "]];
	}
	@catch (NSException *ex) {
		[_delegate macro:self didFailWithErrorMessage:[ex reason]];
	}
}

- (void)httpFailure:(NSError *)error {
	_delegate.showActivityIndicator = NO;
	[_delegate macro:self didFailWithErrorMessage:[error localizedDescription]];
}

@end
